/*
 * @(#)XfplotConfig.java        2003/01/01
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfplot.model;

import xfuzzy.lang.*;

import java.awt.Color;
import java.io.*;

/**
 * Clase que almacena la configuraci�n de la herramienta xfplot
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfplotConfig {
	
	//----------------------------------------------------------------------------//
	//                            CONSTANTES PRIVADAS                             //
	//----------------------------------------------------------------------------//
	
	/**
	 * Constante para indicar el tipo de representaci�n en 3D 
	 */
	private static final int PLOT3D = 0;
	
	/**
	 * Constante para indicar el tipo de representaci�n en 2D
	 */
	private static final int PLOT2D = 1;
	
	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                              //
	//----------------------------------------------------------------------------//

	/**
	 * Referencia al sistema difuso a representar
	 */
	private Specification spec;
	
	/**
	 * �ndice de la variable de entrada a representar en el eje X
	 */
	private int xindex;
	
	/**
	 * �ndice de la variable de entrada a representar en el eje Y
	 */
	private int yindex;
	
	/**
	 * �ndice de la variable de salida a representar en el eje Z
	 */
	private int zindex;
	
	/**
	 * Valores de las variables de entrada que no se van a representar
	 */
	private double[] inputvalue;
	
	/**
	 * Valor del desplazador horizontal
	 */
	private int hslide;
	
	/**
	 * Valor del desplazador vertical
	 */
	private int vslide;
	
	/**
	 * N�mero de puntos en los que se divide cada eje
	 */
	private int samples;
	
	/**
	 * Tipo de representaci�n
	 */
	private int plotmode;
	
	/**
	 * Objeto que almacena el modelo de colores
	 */
	private XfplotColorModel colormodel;
	
	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public XfplotConfig() {
		this.xindex = 0;
		this.yindex = 1;
		this.zindex = 0;
		this.samples = 40;
		this.colormodel = new XfplotColorModel(0);
		this.plotmode = PLOT3D;
		this.hslide = 50;
		this.vslide = 30;
	}
	
 	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//
	
	/**
	 * Asigna el sistema difuso
	 */
	public void setSpecification(Specification spec) {
		this.spec = spec;		
		Variable inputvar[] = spec.getSystemModule().getInputs();
		if(inputvalue == null) {
			this.inputvalue = new double[inputvar.length];
			for(int i=0; i<inputvar.length; i++) inputvalue[i] = inputvar[i].point(0.5);
		}
	}
	
	/**
	 * Obtiene el �ndice de la variable asignada al eje X
	 */
	public int getXIndex() {
		return this.xindex;
	}
	
	/**
	 * Obtiene el �ndice de la variable asignada al eje Y
	 */
	public int getYIndex() {
		return this.yindex;
	}
	
	/**
	 * Obtiene el �ndice de la variable asignada al eje Z
	 */
	public int getZIndex() {
		return this.zindex;
	}
	
	/**
	 * Devuelve la variable a representar en el eje X
	 */
	public Variable getXVariable() {
		Variable inputvar[] = spec.getSystemModule().getInputs();
		return inputvar[xindex];
	}
	/**
	 * Devuelve la variable a representar en el eje Y	
	 */
	public Variable getYVariable() {
		Variable inputvar[] = spec.getSystemModule().getInputs();
		return inputvar[yindex];
	}
	
	/**
	 * Devuelve la variable a representar en el eje Z
	 */
	public Variable getZVariable() {
		Variable outputvar[] = spec.getSystemModule().getOutputs();
		return outputvar[zindex];
	}
	
	/**
	 * Obtiene los valores de las variables de entrada
	 */
	public double[] getInputValues() {
		return this.inputvalue;
	}
	
	/**
	 * Obtiene el n�mero de divisiones de cada eje
	 */
	public int getSamples() {
		return this.samples;
	}
	
	/**
	 * Obtiene el c�digo del tipo de representaci�n
	 */
	public int getPlotMode() {
		return this.plotmode;
	}
	
	/**
	 * Obtiene el c�digo del modelo de colores
	 */
	public int getColorMode() {
		return this.colormodel.getModel();
	}
	
	/**
	 * Devuelve el color del grid (nulo si no debe pintarse)
	 */
	public Color getGridColor() {
		return this.colormodel.getGridColor();
	}
	
	/**
	 * Devuelve el color asociado a un cierto valor
	 */
	public Color getColor(double value) {
		return this.colormodel.getColor(value);
	}
	
	/**
	 * Obtiene la rotaci�n horizontal
	 */
	public int getHSlide() {
		return this.hslide;
	}
	
	/**
	 * Obtiene la rotaci�n vertical
	 */
	public int getVSlide() {
		return this.vslide;
	}
	
	/**
	 * Obtiene los datos de la representaci�n en 2D
	 */
	public double[] get2DFunction() {
		SystemModule system = spec.getSystemModule();
		Variable inputvar[] = system.getInputs();
		Variable outputvar[] = system.getOutputs();
		double zmin = outputvar[zindex].point(0.0);
		double zmax = outputvar[zindex].point(1.0);
		
		double input[] = new double[inputvar.length];
		for(int i=0; i<inputvalue.length && i<input.length; i++) input[i] = inputvalue[i];
		
		double[] function = new double[samples];
		for(int i=0; i<function.length; i++) {
			input[xindex] = inputvar[xindex].point(i*1.0/(samples-1));
			double output = system.crispInference(input)[zindex];
			function[i] = (output - zmin)/(zmax - zmin);
		}
		return function;
	}
	
	/**
	 * Obtiene los datos de la representaci�n en 3D
	 */
	public double[][] get3DFunction() {
		SystemModule system = spec.getSystemModule();
		Variable inputvar[] = system.getInputs();
		Variable outputvar[] = system.getOutputs();
		double zmin = outputvar[zindex].point(0.0);
		double zmax = outputvar[zindex].point(1.0);
		
		double input[] = new double[inputvar.length];
		for(int i=0; i<inputvalue.length && i<input.length; i++) input[i] = inputvalue[i];
		
		double[][] function = new double[samples][samples];
		for(int i=0; i<samples; i++)
			for(int j=0; j<samples; j++) {
				input[xindex] = inputvar[xindex].point(i*1.0/(samples-1));
				input[yindex] = inputvar[yindex].point(j*1.0/(samples-1));
				double output = system.crispInference(input)[zindex];
				function[i][j] = (output - zmin)/(zmax - zmin);
			}
		return function;
	}
	
	/**
	 * Asigna el valor a las variables de entrada
	 */
	public void setInputValues(double[] value) throws Exception {
		if(value.length > inputvalue.length) throw new Exception();
		Variable inputvar[] = spec.getSystemModule().getInputs();
		for(int i=0; i<inputvar.length; i++) {
			double rate = inputvar[i].getRate(value[i]);
			if(rate < 0.0 || rate > 1.0) throw new Exception();
		}
		for(int i=0; i<inputvalue.length; i++) inputvalue[i] = value[i];
	}
	
	/**
	 * Asigna un valor de las variables de entrada
	 * @param index
	 * @param value
	 */
	public void setInputValue(int index, double value) {
		if(inputvalue == null) {
			inputvalue = new double[index+1];
		}
		if(inputvalue.length <= index) {
			double[] aux = new double[index+1];
			System.arraycopy(inputvalue, 0, aux, 0, inputvalue.length);
			inputvalue = aux;
		}
		inputvalue[index] = value;
	}
	
	/**
	 * Asigna las variables a representar
	 */
	public void setVariables(String xvar, String yvar, String zvar)
	throws Exception {
		Variable inputvar[] = spec.getSystemModule().getInputs();
		Variable outputvar[] = spec.getSystemModule().getInputs();
		int xi = -1;
		int yi = -1;
		int zi = -1;
		for(int i=0; i<inputvar.length; i++) {
			if(inputvar[i].toString().equals(xvar)) xi = i;
			if(inputvar[i].toString().equals(yvar)) yi = i;
		}
		for(int i=0; i<outputvar.length; i++) {
			if(outputvar[i].toString().equals(zvar)) zi = i;
		}
		if(xi<0 || yi<0 || xi==yi || zi<0) throw new Exception();
		
		this.xindex = xi;
		this.yindex = yi;
		this.zindex = zi;
		this.plotmode = PLOT3D;
	}

	/**
	 * Asigna las variables a representar
	 */
	public void setVariables(int xi, int yi, int zi) {		
		this.xindex = xi;
		this.yindex = yi;
		this.zindex = zi;
		this.plotmode = PLOT3D;
	}

	/**
	 * Asigna las variables a representar
	 */
	public void setVariables(String xvar, String zvar)
	throws Exception {
		Variable inputvar[] = spec.getSystemModule().getInputs();
		Variable outputvar[] = spec.getSystemModule().getInputs();
		int xi = -1;
		int zi = -1;
		for(int i=0; i<inputvar.length; i++) {
			if(inputvar[i].toString().equals(xvar)) xi = i;
		}
		for(int i=0; i<outputvar.length; i++) {
			if(outputvar[i].toString().equals(zvar)) zi = i;
		}
		if(xi<0 || zi<0) throw new Exception();
		
		this.xindex = xi;
		this.yindex = 0;
		this.zindex = zi;
		this.plotmode = PLOT2D;
	}

	/**
	 * Asigna las variables a representar
	 */
	public void setVariables(int xi, int zi) {	
		this.xindex = xi;
		this.yindex = 0;
		this.zindex = zi;
		this.plotmode = PLOT2D;
	}
	
	/**
	 * Asigna los valores de la perspectiva
	 */
	public void setSlides(int horiz, int vert) throws Exception {
		if(vert<0 || vert>100 || horiz<0 || horiz>100) throw new Exception();
		this.hslide = horiz;
		this.vslide = vert;
	}
	
	/**
	 * Asigna el tipo de representaci�n
	 */
	public void setPlotMode(int mode) throws Exception {
		if(mode != PLOT3D && mode != PLOT2D) throw new Exception();
		this.plotmode = mode;
	}
	
	/**
	 * Asigna el n�mero de divisiones de cada eje
	 */
	public void setSamples(int number) throws Exception {
		if(number<0) throw new Exception();
		this.samples = number;
	}
	
	/**
	 * Asigna el modelo de color
	 */
	public void setColorMode(int code) throws Exception {
		if(code<0) throw new Exception();
		this.colormodel.setModel(code);
	}
	
	/**
	 * Selecciona la variable asignada al eje X
	 */
	public void setXIndex(int index) {
		this.xindex = index;
	}
	
	/**
	 * Selecciona la variable asignada al eje Y
	 */
	public void setYIndex(int index) {
		this.yindex = index;
	}
	
	/**
	 * Selecciona la variable asignada al eje Z
	 */
	public void setZIndex(int index) {
		this.zindex = index;
	}
	
	/**
	 * Devuelve la cabecera del fichero de datos
	 */
	public String getHeading() {
		String eol = System.getProperty("line.separator", "\n");
		Variable inputvar[] = spec.getSystemModule().getInputs();
		Variable outputvar[] = spec.getSystemModule().getInputs();
		Variable xvar = inputvar[xindex];
		Variable yvar = inputvar[yindex];
		Variable zvar = outputvar[zindex];
		
		String code = "// Data generated by Xfplot"+eol;
		code += "// System: "+spec.getName()+eol;
		code += "// X Axis: "+xvar.toString()+eol;
		code += "// Y Axis: "+yvar.toString()+eol;
		code += "// Z Axis: "+zvar.toString()+eol;
		code += "// Input values: "+eol;
		for(int i=0; i<inputvar.length; i++)
			code += "//  "+inputvar[i].toString()+" : "+inputvalue[i]+eol;
		code += eol+eol;
		return code;
	}
		
	/**
	 * Representaci�n en formato XML
	 */
	public String toXML() {
		String eol = System.getProperty("line.separator", "\n");
		String code = "<xfuzzy>"+eol; 
		code += "\t<xfplot system=\""+spec.getName()+"\">"+eol;

		if(plotmode == PLOT3D) {
			code += "\t\t<graph3D xvar=\"i"+xindex+"\" yvar=\"i"+yindex+"\" zvar=\"o"+zindex+"\" >";
			code += "</graph3D>"+eol;
		} else {
			code += "\t\t<graph2D xvar=\"i"+xindex+"\" yvar=\"o"+zindex+"\" >";
			code += "</graph2D>"+eol;
		}

		for(int i=1; i<inputvalue.length; i++) {
			code += "\t\t<input name=\"i"+i+"\" value=\""+inputvalue[i]+"\" ></input>"+eol;
		}
		code += "\t\t<perspective hslide=\""+hslide+"\" vslide=\""+vslide+"\" ></perspective>"+eol;
		code += "\t\t<samples value=\""+samples+"\"></samples>"+eol;
		code += "\t\t<colormode value=\""+colormodel+"\"></colormode>"+eol;
		code += "\t</xfplot>"+eol;
		code += "</xfuzzy>"+eol;
		return code;
	}

	/**
	 * Almacenar la configuracion en un fichero
	 */
	public boolean save(File file, boolean append) {
		String code = toXML();
		byte buf[] = code.getBytes();

		try {
			OutputStream stream = new FileOutputStream(file,append);
			stream.write(buf);
			stream.close();
		}
		catch (IOException e) { return false; }
		return true;
	}
}
